﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Web;
using System.Web.Caching;
using System.Web.Security;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;

/// <summary>
/// ISRoleProvider - провайдер безопасности хранящий роли как роли в БД SQL
/// </summary>
/// 
namespace Ecopsy.Providers
{
    public class XmlRoleProvider : RoleProvider
    {
        private readonly string rootRoleName = "ФронтОффис";
        private object syncObj = new object();
        private Hashtable userRoles = new Hashtable(50);

        public override string ApplicationName
        {
            get
            {
                return ((XmlMembershipProvider)Membership.Provider).ApplicationName;
            }
            set
            {
                ((XmlMembershipProvider)Membership.Provider).ApplicationName = value;
            }
        }

        public override string Description
        {
            get
            {
                return base.Description;
            }
        }

        public override string Name
        {
            get
            {
                return base.Name;
            }
        }

        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            base.Initialize(name, config);
        }

        public override void CreateRole(string roleName)
        {
            string srvName = ((XmlMembershipProvider)Membership.Provider).SQLServerName;
            string dbName = ((XmlMembershipProvider)Membership.Provider).DatabaseName;

            Server srv;
            if (!string.IsNullOrEmpty(((XmlMembershipProvider)Membership.Provider).UserName))
            {
                ServerConnection cs = new ServerConnection(srvName, ((XmlMembershipProvider)Membership.Provider).UserName, ((XmlMembershipProvider)Membership.Provider).Pwd);
                srv = new Server(cs);
            }
            else
            {
                srv = new Server(srvName);
            }

            if (srv.Databases.Contains(dbName))
            {
                if (!srv.Databases[dbName].Roles.Contains(roleName))
                {
                    DatabaseRole role = new DatabaseRole(srv.Databases[dbName], roleName);
                    role.Create();
                    if (srv.Databases[dbName].Roles.Contains(rootRoleName))
                    {
                        srv.Databases[dbName].Roles[rootRoleName].AddMember(roleName);
                    }
                }
            }
        }

        private Server GetDbserver()
        {
            string srvName = ((XmlMembershipProvider)Membership.Provider).SQLServerName;
            string dbName = ((XmlMembershipProvider)Membership.Provider).DatabaseName;


            Server srv;
            if (!string.IsNullOrEmpty(((XmlMembershipProvider)Membership.Provider).UserName))
            {
                ServerConnection cs = new ServerConnection(srvName, ((XmlMembershipProvider)Membership.Provider).UserName, ((XmlMembershipProvider)Membership.Provider).Pwd);
                srv = new Server(cs);
            }
            else
            {
                srv = new Server(srvName);
            }
            return srv;
        }

        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {   
            
            Server srv = GetDbserver();
            
        
            foreach (string UserName in usernames )
            {                
                if(0==Membership.FindUsersByName (UserName).Count)
                    throw new Exception(string.Format("Ошибка. пльзователя '{0}'- не существует ", UserName));
                
                foreach (string RoleName in roleNames)
                {
                    if (null == srv.Roles[RoleName])
                        AddDbRole(srv, RoleName, UserName);
                    else
                        AddServerRole(srv, RoleName, UserName);  
                }
            }
        }

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            try
            {
             
                Server srv = GetDbserver();
                


                foreach (string UserName in usernames)
                {
                    if (0 == Membership.FindUsersByName(UserName).Count) throw new Exception(string.Format("Ошибка. пльзователя '{0}'- не существует ", UserName));

                    foreach (string RoleName in roleNames)
                    {

                        if(srv .Roles [RoleName]==null) 
                            RemoveUserFromDbRole(srv, RoleName, UserName);
                        else
                            RemoveUserFromServerRole(srv, RoleName, UserName);
                    }
                }

            }
            catch (Exception ex)
            {

                throw new Exception("Ошибка DataBaseRole.DropMember() " + ex.Message);
            }
        }

        private void RemoveUserFromServerRole(Server srv, string RoleName, string UserName)
        {
            
            ServerRole r;

            r = srv.Roles[RoleName];

            if (null == r) throw new Exception(string.Format("Ошибка. Роли '{0}'- не существует ", RoleName));
            bool rExsits = false;
            foreach (string s in r.EnumServerRoleMembers())
            {
                rExsits = s.ToUpper().Equals(UserName.ToUpper());
                if (rExsits) r.DropMember(UserName);

            }
        }

        private void RemoveUserFromDbRole( Server srv, string RoleName, string UserName)
        {
            string dbName = ((XmlMembershipProvider)Membership.Provider).DatabaseName;
            DatabaseRole r;

            r = srv.Databases[dbName].Roles[RoleName];

            if (null == r) throw new Exception(string.Format("Ошибка. Роли '{0}'- не существует ", RoleName));
            bool rExsits = false;
            foreach (string s in r.EnumMembers())
            {
                rExsits = s.ToUpper().Equals(UserName.ToUpper());
                if (rExsits) r.DropMember(UserName);

            }

        }


        private void AddServerRole(Server srv, string RoleName, string UserName)
        {
            ServerRole r;            
            r = srv.Roles[RoleName];
            if (null == r)
                throw new Exception(string.Format("Ошибка. Роли '{0}'- не существует ", RoleName));

            bool rExsits = false;
            foreach (string s in r.EnumServerRoleMembers ())
            {
                rExsits = s.ToUpper().Equals(UserName.ToUpper());
            }
            if (!rExsits)
                r.AddMember(UserName);


        }
        private void AddDbRole(Server srv, string RoleName, string UserName)
        {
            DatabaseRole r;
            string dbName = ((XmlMembershipProvider)Membership.Provider).DatabaseName;
            r = srv.Databases[dbName].Roles[RoleName];
            if (null == r)
                throw new Exception(string.Format("Ошибка. Роли '{0}'- не существует ", RoleName));

            bool rExsits = false;
            foreach (string s in r.EnumMembers())
            {
                rExsits = s.ToUpper().Equals(UserName.ToUpper());
            }
            if (!rExsits)
                r.AddMember(UserName);

        }

      
        
        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            if (!RoleExists(roleName))
            {
                throw new Exception(string.Format("Ошибка удаления роли '{0}'. Роль не существует.", roleName));
            }

            string dbName = ((XmlMembershipProvider)Membership.Provider).DatabaseName;
            bool rc = false;

            Server srv;
            if (!string.IsNullOrEmpty(((XmlMembershipProvider)Membership.Provider).UserName))
            {
                ServerConnection cs = new ServerConnection(((XmlMembershipProvider)Membership.Provider).SQLServerName, ((XmlMembershipProvider)Membership.Provider).UserName, ((XmlMembershipProvider)Membership.Provider).Pwd);
                srv = new Server(cs);
            }
            else
            {
                srv = new Server(((XmlMembershipProvider)Membership.Provider).SQLServerName);
            }

            if (srv.Databases.Contains(dbName))
            {
                if (!srv.Databases[((XmlMembershipProvider)Membership.Provider).DatabaseName].Roles.Contains(roleName))
                {
                    if (throwOnPopulatedRole && srv.Databases[((XmlMembershipProvider)Membership.Provider).DatabaseName].Roles[roleName].EnumMembers().Count > 0)
                    {
                        throw new Exception(string.Format("Ошибка удаления роли '{0}'. Роль не пуста!", roleName));
                    }
                    try
                    {
                        srv.Databases[((XmlMembershipProvider)Membership.Provider).DatabaseName].Roles[roleName].Drop();
                    }
                    catch (Exception e)
                    {
                        throw new Exception(string.Format("Ошибка удаления роли '{0}'.", roleName), e);
                    }
                    rc = true;
                }
            }

            return rc;
        }

        public override bool IsUserInRole(string username, string roleName)
        {
            string srvName = ((XmlMembershipProvider)Membership.Provider).SQLServerName;
            string dbName = ((XmlMembershipProvider)Membership.Provider).DatabaseName;
            bool rc = false;

            Server srv;
            if (!string.IsNullOrEmpty(((XmlMembershipProvider)Membership.Provider).UserName))
            {
                ServerConnection cs = new ServerConnection(srvName, ((XmlMembershipProvider)Membership.Provider).UserName, ((XmlMembershipProvider)Membership.Provider).Pwd);
                srv = new Server(cs);
            }
            else
            {
                srv = new Server(srvName);
            }

            if (srv.Databases.Contains(dbName))
            {
                if (!srv.Databases[dbName].Users.Contains(username))
                {
                    rc = srv.Databases[dbName].Users[username].IsMember(roleName);
                }
            }

            return rc;
        }

        public override bool RoleExists(string roleName)
        {
            bool rc = false;

            Server srv;
            if (!string.IsNullOrEmpty(((XmlMembershipProvider)Membership.Provider).UserName))
            {
                ServerConnection cs = new ServerConnection(((XmlMembershipProvider)Membership.Provider).SQLServerName, ((XmlMembershipProvider)Membership.Provider).UserName, ((XmlMembershipProvider)Membership.Provider).Pwd);
                srv = new Server(cs);
            }
            else
            {
                srv = new Server(((XmlMembershipProvider)Membership.Provider).SQLServerName);
            }

            string dbName = ((XmlMembershipProvider)Membership.Provider).DatabaseName;

            if (srv.Databases.Contains(dbName))
            {
                rc = srv.Databases[dbName].Roles.Contains(roleName);
            }

            return rc;
        }

        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public override string[] GetAllRoles()
        {
            List<string> resStr = new List<string>(4);

            Server srv;
            if (!string.IsNullOrEmpty(((XmlMembershipProvider)Membership.Provider).UserName))
            {
                ServerConnection cs = new ServerConnection(((XmlMembershipProvider)Membership.Provider).SQLServerName, ((XmlMembershipProvider)Membership.Provider).UserName, ((XmlMembershipProvider)Membership.Provider).Pwd);
                srv = new Server(cs);
            }
            else
            {
                srv = new Server(((XmlMembershipProvider)Membership.Provider).SQLServerName);
            }

            if (srv.Databases.Contains(((XmlMembershipProvider)Membership.Provider).DatabaseName))
            {
                if (srv.Databases[((XmlMembershipProvider)Membership.Provider).DatabaseName].Roles.Contains(rootRoleName))
                {
                    foreach (string roleName in srv.Databases[((XmlMembershipProvider)Membership.Provider).DatabaseName].Roles[rootRoleName].EnumMembers())
                    {
                        if (srv.Databases[((XmlMembershipProvider)Membership.Provider).DatabaseName].Roles.Contains(roleName))
                        {
                            resStr.Add(roleName);
                        }
                    }
                }
            }

            string[] fr = new string[resStr.Count];
            for (int i = 0; i < resStr.Count; ++i)
            {
                fr[i] = resStr[i];
            }

            return fr;
        }

        public void ResetRolesCache(string username)
        {
            lock (syncObj)
            {
                if (userRoles.Contains(username))
                {
                    userRoles.Remove(username);
                }
            }
        }

        public override string[] GetRolesForUser(string username)
        {
            Server srv;
            List<string> r = new List<string>();
            
            if (!string.IsNullOrEmpty(((XmlMembershipProvider)Membership.Provider).UserName))
            {
                ServerConnection cs = new ServerConnection(((XmlMembershipProvider)Membership.Provider).SQLServerName, ((XmlMembershipProvider)Membership.Provider).UserName, ((XmlMembershipProvider)Membership.Provider).Pwd);
                srv = new Server(cs);
            }
            else
            {
                srv = new Server(((XmlMembershipProvider)Membership.Provider).SQLServerName);
            }

            DatabaseRoleCollection coll = srv.Databases[((XmlMembershipProvider)Membership.Provider).DatabaseName].Roles;
            User u = srv.Databases[((XmlMembershipProvider)Membership.Provider).DatabaseName].Users[username];
            foreach (DatabaseRole rl in coll)
            {                
                if(u.IsMember(rl.Name)) r.Add (rl.Name); 
            }             

            return r.ToArray(); 


            #region
            //   string[] fr;

         //   lock (syncObj)
         //   {
         //       if (userRoles.Contains(username))
         //       {
         //           fr = (string[])userRoles[username];
         //       }
         //       else
         //       {
         //           List<string> resStr = new List<string>(5);

         //           Server srv;
         //           if (!string.IsNullOrEmpty(((XmlMembershipProvider)Membership.Provider).UserName))
         //           {
         //               ServerConnection cs =
         //                   new ServerConnection(((XmlMembershipProvider)Membership.Provider).SQLServerName,
         //                                        ((XmlMembershipProvider)Membership.Provider).UserName,
         //                                        ((XmlMembershipProvider)Membership.Provider).Pwd);
         //               srv = new Server(cs);
         //           }
         //           else
         //           {
         //               srv = new Server(((XmlMembershipProvider)Membership.Provider).SQLServerName);
         //           }

         //           //if (srv.Databases.Contains(((XmlMembershipProvider)Membership.Provider).DatabaseName))
         //           //{
         //           //    if (
         //           //        srv.Databases[((XmlMembershipProvider)Membership.Provider).DatabaseName].Roles.Contains(
         //           //            rootRoleName))
         //           //    {
         //           //        foreach (
         //           //            string roleName in
         //           //                srv.Databases[((XmlMembershipProvider)Membership.Provider).DatabaseName].Roles[
         //           //                    rootRoleName]
         //           //                    .EnumMembers())
         //           //        {
         //           //            if (
         //           //                srv.Databases[((XmlMembershipProvider)Membership.Provider).DatabaseName].Users.Contains(
         //           //                    username) &&
         //           //                srv.Databases[((XmlMembershipProvider)Membership.Provider).DatabaseName].Users[username]
         //           //                    .
         //           //                    IsMember(roleName))
         //           //            {
         //           //                resStr.Add(roleName);
         //           //            }
         //           //        }
         //           //    }
         //           //}

         //           //fr = new string[resStr.Count];
         //           //for (int i = 0; i < resStr.Count; ++i)
         //           //{
         //           //    fr[i] = resStr[i];
         //           //}

         //           //userRoles[username] = fr;
         //       }
         //   }
            ////   return fr;
            #endregion
        }

        public override string[] GetUsersInRole(string roleName)
        {
            List<string> resStr = new List<string>(4);

            Server srv;
            if (!string.IsNullOrEmpty(((XmlMembershipProvider)Membership.Provider).UserName))
            {
                ServerConnection cs = new ServerConnection(((XmlMembershipProvider)Membership.Provider).SQLServerName, ((XmlMembershipProvider)Membership.Provider).UserName, ((XmlMembershipProvider)Membership.Provider).Pwd);
                srv = new Server(cs);
            }
            else
            {
                srv = new Server(((XmlMembershipProvider)Membership.Provider).SQLServerName);
            }

            if (srv.Databases.Contains(((XmlMembershipProvider)Membership.Provider).DatabaseName))
            {
                if (srv.Databases[((XmlMembershipProvider)Membership.Provider).DatabaseName].Roles.Contains(roleName))
                {
                    foreach (string members in srv.Databases[((XmlMembershipProvider)Membership.Provider).DatabaseName].Roles[roleName].EnumMembers())
                    {
                        resStr.Add(members);
                    }
                }
            }

            string[] fr = new string[resStr.Count];
            for (int i = 0; i < resStr.Count; ++i)
            {
                fr[i] = resStr[i];
            }

            return fr;
        }
    }
}
